package com.ljc.db.backend.dm.pageIndex;

import com.ljc.db.backend.dm.pageCache.PageCache;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 获取页面空闲分区数量信息或者根据每个页的空闲分区数量将其加入对应下标的空闲分区
 */
public class PageIndex {
    // 将一页划成40个区间
    private static final int INTERVALS_NO = 40;
    // 每个区间的大小
    private static final int THRESHOLD = PageCache.PAGE_SIZE / INTERVALS_NO;

    private Lock lock;
    // 类似于HashMap，数组 + 链表的结构。每个数组的 cell 存储一个链表，这个链表中保存数据页 pageInfo 的信息
    // lists的下标对应了空闲分区的数量
    private List<PageInfo>[] lists;

    @SuppressWarnings("unchecked")
    public PageIndex() {
        lock = new ReentrantLock();
        // 开辟大小为 41 的数组【因为区间号从 1 开始，即 1 - 40 的下标为区间号，里面保存的是数据页pageInfo信息】
        lists = new List[INTERVALS_NO + 1];
        for (int i = 0; i < INTERVALS_NO + 1; i++) {
            lists[i] = new ArrayList<>();
        }
    }

    /**
     *  将一个页计算出空闲分区数量后加入对应的pageIndex中【每个页的剩余空间大小不同，存入的下标也就不同】
     */
    public void add(int pgno, int freeSpace) {
        lock.lock();
        try {
            int number = freeSpace / THRESHOLD;
            lists[number].add(new PageInfo(pgno, freeSpace));
        } finally {
            lock.unlock();
        }
    }

    /**
     * 获取一个有足够多空闲分区的页
     */
    public PageInfo select(int spaceSize) {
        lock.lock();
        try {
            // 利用空间的大小和区间的大小 计算出区间号【区间号其实就是每页空闲分区的数量】
            int number = spaceSize / THRESHOLD;
            // 如果区间号在区间范围内，区间号 + 1【向上取整，防止空闲分区数量不够】
            if(number < INTERVALS_NO) number++;
            // 从
            while(number <= INTERVALS_NO) {
                if(lists[number].size() == 0) {
                    number++;
                    continue;
                }
                // 找到一个空闲分区数量足够多的页，使用后将其从pageIndex中移除，再次使用时需要重写插入
                return lists[number].remove(0);
            }
            // 没有找到有足够多空闲分区的页
            return null;
        } finally {
            lock.unlock();
        }
    }

}
